观察者和发布订阅

发布订阅和观察者,确和研究vue源码有点关系。
之前当作是一个模式学习,细比较之:

2020.9.21 星期一 13:32

vue相关

vue 中的 observer watcher dep 可以理解为发布订阅者模式吧?
应该是观察者模式。 vue的事件通讯机制才是发布订阅模式
vue双向绑定过程涉及发布订阅和观察者,setter是观察者,更新的过程是发布订阅

区别解释

观察者模式

定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知。

模式特征:
\
一个目标者对象 Subject,拥有方法:添加 / 删除 / 通知 Observer;
多个观察者对象 Observer,拥有方法:接收 Subject 状态变更通知并处理;
目标对象 Subject 状态变更时,通知所有 Observer。

Subject 添加一系列 Observer, Subject 负责维护与这些 Observer 之间的联系,“你对我有兴趣,我更新就会通知你”。

发布订阅模式

基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。

发布订阅模式与观察者模式的不同,“第三者” (事件中心)出现。目标对象并不直接通知观察者,而是通过事件中心来派发通知。

比较

观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

从上面代码可以看出,观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,会造成代码的冗余。而发布订阅模式则统一由调度中心处理,消除了发布者和订阅者之间的依赖。

原文:Observer vs Pub-Sub pattern

Publishers + Subscribers = Observer Pattern

所谓观察者模式,其实就是为了实现松耦合(loosely coupled)。
用《Head First设计模式》里的气象站为例子,每当气象测量数据有更新,changed()方法就会被调用,于是我们可以在changed()方法里面,更新气象仪器上的数据,比如温度、气压等等。

在发布订阅模式里,发布者,并不会直接通知订阅者,换句话说,发布者和订阅者,彼此互不相识。

从表面上看:
观察者模式里,只有两个角色 —— 观察者 + 被观察者而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的 —— 经纪人Broker

往更深层次讲:
观察者和被观察者,是松耦合的关系发布者和订阅者,则完全不存在耦合

从使用层面上讲:
观察者模式,多用于单个应用内部
发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如我们常用的消息中间件

区别

  1. 在Observer模式中,Observers知道Subject,同时Subject还保留了Observers的记录。
    然而,在发布者/订阅者中,发布者和订阅者不需要彼此了解。他们只是在消息队列或代理的帮助下进行通信。
  2. 在Publisher / Subscriber模式中,组件是松散耦合的,而不是Observer模式。
  3. 观察者模式主要以同步方式实现,即当某些事件发生时,Subject调用其所有观察者的适当方法。
    发布者/订阅者在大多情况下是异步方式(使用消息队列)。
  4. 观察者模式需要在单个应用程序地址空间中实现。
    另一方面,发布者/订阅者模式更像是跨应用程序模式。

订阅-发布模式

类似点
观察者模式与订阅-发布模式都是定义了一个一对多的依赖关系,当有关状态发生变更时则执行相应的更新。
区别点
发布订阅模式更灵活,是进阶版的观察者模式,指定对应分发。

  1. 观察者模式维护单一事件对应多个依赖该事件的对象关系;
  2. 发布订阅维护多个事件(主题)及依赖各事件(主题)的对象之间的关系;
  3. 观察者模式是目标对象直接触发通知(全部通知),观察对象被迫接收通知。发布订阅模式多了个中间层(事件中心),由其去管理通知广播(只通知订阅对应事件的对象);
  4. 观察者模式对象间依赖关系较强,发布订阅模式中对象之间实现真正的解耦

SMTC

观察者模式指的是一个对象(Subject)维持一系列依赖于它的对象(Observer),当有关状态发生变更时 Subject 对象则通知一系列 Observer 对象进行更新。

在观察者模式中,Subject 对象拥有添加、删除和通知一系列 Observer 的方法等等,而 Observer 对象拥有更新方法等等。

发布订阅模式指的是希望接收通知的对象(Subscriber)基于一个 主题 通过自定义事件订阅主题,被激活事件的对象(Publisher)通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// ## 观察者模式
class Subject {
constructor() {
this.list = [];
}
add(ob) {
this.list.push(ob);
}
remove(ob) {
this.list.splice(this.list.indexOf(this.list.find((item) => ob.id === item.id)), 1);
}
notfiy() {
this.list.forEach((i) => {
i.update(i.id);
});
}
}
class Observer {
constructor(id) {
this.id = id;
}
update(id) {
console.log(`我是${id},我被通知了!`);
}
}

let people1 = new Observer(123);
let people2 = new Observer(9527);
let people3 = new Observer(89757);

let sub = new Subject();

sub.add(people1);
sub.add(people2);
sub.add(people3);

sub.notfiy();

setTimeout(() => {
sub.remove(people2);
sub.notfiy();
}, 200);

// ## 发布订阅模式
class Pub {
constructor() {
this.list = {};
}
subscribe(name, cb) {
if (!this.list[name]) {
this.list[name] = [];
}
this.list[name].push(cb);
}
unSubscribe(name) {
this.list[name] = [];
}
publish(...args) {
const name = Array.from(args).shift();
if (!this.list[name] || this.list[name].length === 0) {
console.log(`${name}主题没有可发布的事件!`);
return;
}
this.list[name].forEach((item) => {
item(args);
});
}
}

let publisher = new Pub();

publisher.subscribe('event1', (e) => {
console.log(e);
});
publisher.subscribe('event1', (e) => {
console.log(e);
});
publisher.subscribe('event2', (e) => {
console.log(e);
});
publisher.subscribe('event3', (e) => {
console.log(e);
});

publisher.publish('event1', '额外参数1');
setTimeout(() => {
publisher.unSubscribe('event2');
publisher.publish('event2', '额外参数2');
publisher.publish('event3', '额外参数3', '额外参数4');
}, 2000);

网上关于这个问题的回答,出现了两极分化,有认为发布订阅模式就是观察者模式的,也有认为观察者模式和发布订阅模式是真不一样的。

其实我不知道发布订阅模式是不是观察者模式,就像我不知道辨别模式的关键是设计意图还是设计结构(理念),虽然《JavaScript设计模式与开发实践》一书中说了分辨模式的关键是意图而不是结构

如果以结构来分辨模式,发布订阅模式相比观察者模式多了一个中间件订阅器,所以发布订阅模式是不同于观察者模式的;如果以意图来分辨模式,他们都是实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,那么他们就是同一种模式,发布订阅模式是在观察者模式的基础上做的优化升级。

不过,不管他们是不是同一个设计模式,他们的实现方式确实有差别,我们在使用的时候应该根据场景来判断选择哪个。

knowledge is no pay,reward is kindness
0%